home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / ANSI / c-client / mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-26  |  47.0 KB  |  1,587 lines

  1. /*
  2.  * Program:    Mailbox Access routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 November 1989
  13.  * Last Edited:    26 May 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <time.h>
  42. #include "misc.h"
  43.  
  44.  
  45. /* c-client global data */
  46.  
  47. DRIVER *maildrivers = NIL;    /* list of mail drivers */
  48. DRIVER *inboxdriver = NIL;    /* INBOX mail driver */
  49. char *lhostn = NIL;        /* local host name */
  50. mailgets_t mailgets = NIL;    /* pointer to alternate gets function */
  51.                 /* mail cache manipulation function */
  52. mailcache_t mailcache = mm_cache;
  53.  
  54. /* Default limited get string
  55.  * Accepts: readin function pointer
  56.  *        stream to use
  57.  *        number of bytes
  58.  * Returns: string read in, truncated if necessary
  59.  *
  60.  * This is a sample mailgets routine.  It simply truncates any data larger
  61.  * than MAXMESSAGESIZE.  On most systems, you generally don't use a mailgets
  62.  * routine at all, but on some systems (e.g. DOS) it's required to prevent the
  63.  * application from crashing.  This one is filled in by the os driver for any
  64.  * OS that requires a mailgets routine and the main program has not already
  65.  * supplied one, generally in tcp_open().
  66.  */
  67.  
  68. char *mm_gets (readfn_t f,void *stream,unsigned long size)
  69. {
  70.   char *s;
  71.   char tmp[MAILTMPLEN+1];
  72.   unsigned long i,j = 0;
  73.                 /* truncate? */
  74.   if (i = (size > MAXMESSAGESIZE)) {
  75.     sprintf (tmp,"%ld character literal truncated to %ld characters",
  76.          size,MAXMESSAGESIZE);
  77.     mm_log (tmp,WARN);        /* warn user */
  78.     i = size - MAXMESSAGESIZE;    /* number of bytes of slop */
  79.     size = MAXMESSAGESIZE;    /* maximum length string we can read */
  80.   }
  81.   s = (char *) fs_get (size + 1);
  82.   *s = s[size] = '\0';        /* init in case getbuffer fails */
  83.   (*f) (stream,size,s);        /* get the literal */
  84.                 /* toss out everything after that */
  85.   while (i -= j) (*f) (stream,j = min ((long) MAILTMPLEN,i),tmp);
  86.   return s;
  87. }
  88.  
  89. /* Default mail cache handler
  90.  * Accepts: pointer to cache handle
  91.  *        message number
  92.  *        caching function
  93.  * Returns: cache data
  94.  */
  95.  
  96. void *mm_cache (MAILSTREAM *stream,long msgno,long op)
  97. {
  98.   size_t new;
  99.   void *ret = NIL;
  100.   long i = msgno - 1;
  101.   unsigned long j = stream->cachesize;
  102.   switch ((int) op) {        /* what function? */
  103.   case CH_INIT:            /* initialize cache */
  104.     if (stream->cachesize) {    /* flush old cache contents */
  105.       while (stream->cachesize) mm_cache (stream,stream->cachesize--,CH_FREE);
  106.       fs_give ((void **) &stream->cache.c);
  107.       stream->nmsgs = 0;    /* can't have any messages now */
  108.     }
  109.     break;
  110.   case CH_SIZE:            /* (re-)size the cache */
  111.     if (msgno > j) {        /* do nothing if size adequate */
  112.       new = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
  113.       if (stream->cache.c) fs_resize ((void **) &stream->cache.c,new);
  114.       else stream->cache.c = (void **) fs_get (new);
  115.                 /* init cache */
  116.       while (j < stream->cachesize) stream->cache.c[j++] = NIL;
  117.     }
  118.     break;
  119.   case CH_MAKELELT:        /* return long elt, make if necessary */
  120.     if (!stream->cache.c[i]) {    /* have one already? */
  121.                 /* no, instantiate it */
  122.       stream->cache.l[i] = (LONGCACHE *) fs_get (sizeof (LONGCACHE));
  123.       memset (&stream->cache.l[i]->elt,0,sizeof (MESSAGECACHE));
  124.       stream->cache.l[i]->elt.lockcount = 1;
  125.       stream->cache.l[i]->elt.msgno = msgno;
  126.       stream->cache.l[i]->env = NIL;
  127.       stream->cache.l[i]->body = NIL;
  128.     }
  129.                 /* drop in to CH_LELT */
  130.   case CH_LELT:            /* return long elt */
  131.     ret = stream->cache.c[i];    /* return void version */
  132.     break;
  133.  
  134.   case CH_MAKEELT:        /* return short elt, make if necessary */
  135.     if (!stream->cache.c[i]) {    /* have one already? */
  136.       if (stream->scache) {    /* short cache? */
  137.     stream->cache.s[i] = (MESSAGECACHE *) fs_get (sizeof(MESSAGECACHE));
  138.     memset (stream->cache.s[i],0,sizeof (MESSAGECACHE));
  139.     stream->cache.s[i]->lockcount = 1;
  140.     stream->cache.s[i]->msgno = msgno;
  141.       }
  142.       else mm_cache (stream,msgno,CH_MAKELELT);
  143.     }
  144.                 /* drop in to CH_ELT */
  145.   case CH_ELT:            /* return short elt */
  146.     ret = stream->cache.c[i] && !stream->scache ?
  147.       (void *) &stream->cache.l[i]->elt : stream->cache.c[i];
  148.     break;
  149.   case CH_FREE:            /* free (l)elt */
  150.     if (stream->scache) mail_free_elt (&stream->cache.s[i]);
  151.     else mail_free_lelt (&stream->cache.l[i]);
  152.     break;
  153.   case CH_EXPUNGE:        /* expunge cache slot */
  154.                 /* slide down remainder of cache */
  155.     for (i = msgno; i < stream->nmsgs; ++i)
  156.       if (stream->cache.c[i-1] = stream->cache.c[i])
  157.     ((MESSAGECACHE *) mm_cache (stream,i,CH_ELT))->msgno = i;
  158.     stream->cache.c[stream->nmsgs-1] = NIL;
  159.     break;
  160.   default:
  161.     fatal ("Bad mm_cache op");
  162.     break;
  163.   }
  164.   return ret;
  165. }
  166.  
  167. /* Dummy string driver for complete in-memory strings */
  168.  
  169. STRINGDRIVER mail_string = {
  170.   mail_string_init,        /* initialize string structure */
  171.   mail_string_next,        /* get next byte in string structure */
  172.   mail_string_setpos        /* set position in string structure */
  173. };
  174.  
  175.  
  176. /* Initialize mail string structure for in-memory string
  177.  * Accepts: string structure
  178.  *        pointer to string
  179.  *        size of string
  180.  */
  181.  
  182. void mail_string_init (STRING *s,void *data,unsigned long size)
  183. {
  184.                 /* set initial string pointers */
  185.   s->chunk = s->curpos = (char *) (s->data = data);
  186.                 /* and sizes */
  187.   s->size = s->chunksize = s->cursize = size;
  188.   s->data1 = s->offset = 0;    /* never any offset */
  189. }
  190.  
  191. /* Get next character from string
  192.  * Accepts: string structure
  193.  * Returns: character, string structure chunk refreshed
  194.  */
  195.  
  196. char mail_string_next (STRING *s)
  197. {
  198.   return *s->curpos++;        /* return the last byte */
  199. }
  200.  
  201.  
  202. /* Set string pointer position
  203.  * Accepts: string structure
  204.  *        new position
  205.  */
  206.  
  207. void mail_string_setpos (STRING *s,unsigned long i)
  208. {
  209.   s->curpos = s->chunk + i;    /* set new position */
  210.   s->cursize = s->chunksize - i;/* and new size */
  211. }
  212.  
  213. /* Mail routines
  214.  *
  215.  *  mail_xxx routines are the interface between this module and the outside
  216.  * world.  Only these routines should be referenced by external callers.
  217.  *
  218.  *  Note that there is an important difference between a "sequence" and a
  219.  * "message #" (msgno).  A sequence is a string representing a sequence in
  220.  * {"n", "n:m", or combination separated by commas} format, whereas a msgno
  221.  * is a single integer.
  222.  *
  223.  */
  224.  
  225. /* Mail link driver
  226.  * Accepts: driver to add to list
  227.  */
  228.  
  229. void mail_link (DRIVER *driver)
  230. {
  231.   DRIVER **d = &maildrivers;
  232.   while (*d) d = &(*d)->next;    /* find end of list of drivers */
  233.   *d = driver;            /* put driver at the end */
  234.   driver->next = NIL;        /* this driver is the end of the list */
  235. }
  236.  
  237.  
  238. /* Mail manipulate driver parameters
  239.  * Accepts: mail stream
  240.  *        function code
  241.  *        function-dependent value
  242.  * Returns: function-dependent return value
  243.  */
  244.  
  245. void *mail_parameters (MAILSTREAM *stream,long function,void *value)
  246. {
  247.   return stream->dtb ? (stream->dtb->parameters) (function,value) : NIL;
  248. }
  249.  
  250. /* Mail find list of subscribed mailboxes
  251.  * Accepts: mail stream
  252.  *        pattern to search
  253.  */
  254.  
  255. void mail_find (MAILSTREAM *stream,char *pat)
  256. {
  257.   DRIVER *d = maildrivers;
  258.                 /* if have a stream, do it for that stream */
  259.   if (stream && stream->dtb) (*stream->dtb->find) (stream,pat);
  260.                 /* otherwise do for all DTB's */
  261.   else do (d->find) (NIL,pat);
  262.   while (d = d->next);        /* until at the end */
  263. }
  264.  
  265.  
  266. /* Mail find list of subscribed bboards
  267.  * Accepts: mail stream
  268.  *        pattern to search
  269.  */
  270.  
  271. void mail_find_bboards (MAILSTREAM *stream,char *pat)
  272. {
  273.   DRIVER *d = maildrivers;
  274.   if (stream && stream->dtb) (*stream->dtb->find_bboard) (stream,pat);
  275.   else do (d->find_bboard) (NIL,pat);
  276.   while (d = d->next);        /* until at the end */
  277. }
  278.  
  279. /* Mail find list of all mailboxes
  280.  * Accepts: mail stream
  281.  *        pattern to search
  282.  */
  283.  
  284. void mail_find_all (MAILSTREAM *stream,char *pat)
  285. {
  286.   DRIVER *d = maildrivers;
  287.                 /* if have a stream, do it for that stream */
  288.   if (stream && stream->dtb) (*stream->dtb->find_all) (stream,pat);
  289.                 /* otherwise do for all DTB's */
  290.   else do (d->find_all) (NIL,pat);
  291.   while (d = d->next);        /* until at the end */
  292. }
  293.  
  294.  
  295. /* Mail find list of all bboards
  296.  * Accepts: mail stream
  297.  *        pattern to search
  298.  */
  299.  
  300. void mail_find_all_bboard (MAILSTREAM *stream,char *pat)
  301. {
  302.   DRIVER *d = maildrivers;
  303.                 /* if have a stream, do it for that stream */
  304.   if (stream && stream->dtb) (*stream->dtb->find_all_bboard) (stream,pat);
  305.                 /* otherwise do for all DTB's */
  306.   else do (d->find_all_bboard) (NIL,pat);
  307.   while (d = d->next);        /* until at the end */
  308. }
  309.  
  310. /* Mail validate mailbox name
  311.  * Accepts: MAIL stream
  312.  *        mailbox name
  313.  *        purpose string for error message
  314.  *        flag indicating this is not an open
  315.  * Return: driver factory on success, NIL on failure
  316.  */
  317.  
  318. DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose,long nopen)
  319. {
  320.   char tmp[MAILTMPLEN];
  321.   DRIVER *factory;
  322.   for (factory = maildrivers; factory && !(*factory->valid) (mailbox);
  323.        factory = factory->next);
  324.                 /* must match stream and not be dummy */
  325.   if (factory && ((stream && (stream->dtb != factory)) ||
  326.           (nopen && !strcmp (factory->name,"dummy")))) factory = NIL;
  327.   if (!factory && purpose) {    /* if want an error message */
  328.     sprintf (tmp,"Can't %s %s: no such mailbox",purpose,mailbox);
  329.     mm_log (tmp,ERROR);
  330.   }
  331.   return factory;        /* return driver factory */
  332. }
  333.  
  334.  
  335. /* Mail validate network mailbox name
  336.  * Accepts: mailbox name
  337.  *        mailbox driver to validate against
  338.  *        pointer to where to return host name if non-NIL
  339.  *        pointer to where to return mailbox name if non-NIL
  340.  * Returns: driver on success, NIL on failure
  341.  */
  342.  
  343. DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox)
  344. {
  345.   NETMBX mb;
  346.   if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
  347.     return NIL;
  348.   if (host) strcpy (host,mb.host);
  349.   if (mailbox) strcpy (mailbox,mb.mailbox);
  350.   return drv;
  351. }
  352.  
  353. /* Mail validate network mailbox name
  354.  * Accepts: mailbox name
  355.  *        NETMBX structure to return values
  356.  * Returns: T on success, NIL on failure
  357.  */
  358.  
  359. long mail_valid_net_parse (char *name,NETMBX *mb)
  360. {
  361.   long i;
  362.   char c,*s,*t,*v;
  363.   mb->port = 0;            /* initialize structure */
  364.   *mb->host = *mb->mailbox = *mb->service = '\0';
  365.   mb->anoflag = NIL;        /* not (yet) anonymous */
  366.                 /* check if bboard */
  367.   if (mb->bbdflag = (*name == '*') ? T : NIL) name++;
  368.                 /* have host specification? */
  369.   if (!(*name == '{' && (t = strchr (s = name+1,'}')) && (i = t - s)))
  370.     return NIL;            /* not valid host specification */
  371.   strncpy (mb->host,s,i);    /* set host name */
  372.   mb->host[i] = '\0';        /* tie it off */
  373.   strcpy (mb->mailbox,t+1);    /* set mailbox name */
  374.                 /* any switches or port specification? */
  375.   if (t = strpbrk (mb->host,"/:")) {
  376.     c = *t;            /* yes, remember delimiter */
  377.     *t++ = '\0';        /* tie off host name */
  378.     lcase (t);            /* coerce remaining stuff to lowercase */
  379.     do switch (c) {        /* act based upon the character */
  380.     case ':':            /* port specification */
  381.       if (mb->port || ((mb->port = strtol (t,&t,10)) <= 0)) return NIL;
  382.       c = t ? *t++ : '\0';    /* get delimiter, advance pointer */
  383.       break;
  384.  
  385.     case '/':            /* switch */
  386.                 /* find delimiter */
  387.       if (t = strpbrk (s = t,"/:=")) {
  388.     c = *t;            /* remember delimiter for later */
  389.     *t++ = '\0';        /* tie off switch name */
  390.       }
  391.       else c = '\0';        /* no delimiter */
  392.       if (c == '=') {        /* parse switches which take arguments */
  393.     if (t = strpbrk (v = t,"/:")) {
  394.       c = *t;        /* remember delimiter for later */
  395.       *t++ = '\0';        /* tie off switch name */
  396.     }
  397.     else c = '\0';        /* no delimiter */
  398.     if (!strcmp (s,"service")) {
  399.       if (*mb->service) return NIL;
  400.       else strcpy (mb->service,v);
  401.     }
  402.     else return NIL;    /* invalid argument switch */
  403.       }
  404.       else {            /* non-argument switch */
  405.     if (!strcmp (s,"anonymous")) mb->anoflag = T;
  406.     else if (!strcmp (s,"imap2") || !strcmp (s,"nntp")) {
  407.       if (*mb->service) return NIL;
  408.       else strcpy (mb->service,s);
  409.     } 
  410.     else return NIL;    /* invalid non-argument switch */
  411.       }
  412.       break;
  413.     default:            /* anything else is bogus */
  414.       return NIL;
  415.     } while (c);        /* see if anything more to parse */
  416.   }
  417.                 /* default service name */
  418.   if (!*mb->service) strcpy (mb->service,"imap2");
  419.   return T;
  420. }
  421.  
  422. /* Mail subscribe to mailbox
  423.  * Accepts: mail stream
  424.  *        mailbox to add to subscription list
  425.  * Returns: T on success, NIL on failure
  426.  */
  427.  
  428. long mail_subscribe (MAILSTREAM *stream,char *mailbox)
  429. {
  430.   DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox",LONGT);
  431.   return factory ? (*factory->subscribe) (stream,mailbox) : NIL;
  432. }
  433.  
  434.  
  435. /* Mail unsubscribe to mailbox
  436.  * Accepts: mail stream
  437.  *        mailbox to delete from subscription list
  438.  * Returns: T on success, NIL on failure
  439.  */
  440.  
  441. long mail_unsubscribe (MAILSTREAM *stream,char *mailbox)
  442. {
  443.  DRIVER *factory = mail_valid (stream,mailbox,"unsubscribe to mailbox",LONGT);
  444.   return factory ? (*factory->unsubscribe) (stream,mailbox) : NIL;
  445. }
  446.  
  447.  
  448. /* Mail subscribe to bboard
  449.  * Accepts: mail stream
  450.  *        bboard to add to subscription list
  451.  * Returns: T on success, NIL on failure
  452.  */
  453.  
  454. long mail_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  455. {
  456.   char tmp[MAILTMPLEN];
  457.   DRIVER *factory;
  458.   sprintf (tmp,"*%s",mailbox);
  459.   return (factory = mail_valid (stream,tmp,"subscribe to bboard",LONGT)) ?
  460.     (*factory->subscribe_bboard) (stream,mailbox) : NIL;
  461. }
  462.  
  463.  
  464. /* Mail unsubscribe to bboard
  465.  * Accepts: mail stream
  466.  *        bboard to delete from subscription list
  467.  * Returns: T on success, NIL on failure
  468.  */
  469.  
  470. long mail_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  471. {
  472.   char tmp[MAILTMPLEN];
  473.   DRIVER *factory;
  474.   sprintf (tmp,"*%s",mailbox);
  475.   return (factory = mail_valid (stream,tmp,"unsubscribe to bboard",LONGT)) ?
  476.     (*factory->unsubscribe_bboard) (stream,mailbox) : NIL;
  477. }
  478.  
  479. /* Mail create mailbox
  480.  * Accepts: mail stream
  481.  *        mailbox name to create
  482.  * Returns: T on success, NIL on failure
  483.  */
  484.  
  485. long mail_create (MAILSTREAM *stream,char *mailbox)
  486. {
  487.   int localp = *mailbox != '{';
  488.   char tmp[MAILTMPLEN];
  489.   if (!stream &&        /* guess at driver if stream not specified */
  490.       !(stream = mail_open (NIL,localp ? "INBOX" : mailbox,OP_PROTOTYPE))) {
  491.     sprintf (tmp,"Can't create mailbox %s: indeterminate format",mailbox);
  492.     mm_log (tmp,ERROR);
  493.     return NIL;
  494.   }
  495.                 /* must not already exist if local */
  496.   if (localp && mail_valid (stream,mailbox,NIL,LONGT)) {
  497.     sprintf (tmp,"Can't create mailbox %s: mailbox already exists",mailbox);
  498.     mm_log (tmp,ERROR);
  499.     return NIL;
  500.   }
  501.   return stream->dtb ? (*stream->dtb->create) (stream,mailbox) : NIL;
  502. }
  503.  
  504.  
  505. /* Mail delete mailbox
  506.  * Accepts: mail stream
  507.  *        mailbox name to delete
  508.  * Returns: T on success, NIL on failure
  509.  */
  510.  
  511. long mail_delete (MAILSTREAM *stream,char *mailbox)
  512. {
  513.   DRIVER *factory = mail_valid (stream,mailbox,"delete mailbox",LONGT);
  514.   return factory ? (*factory->delete) (stream,mailbox) : NIL;
  515. }
  516.  
  517.  
  518. /* Mail rename mailbox
  519.  * Accepts: mail stream
  520.  *        old mailbox name
  521.  *        new mailbox name
  522.  * Returns: T on success, NIL on failure
  523.  */
  524.  
  525. long mail_rename (MAILSTREAM *stream,char *old,char *new)
  526. {
  527.   char tmp[MAILTMPLEN];
  528.   DRIVER *factory = mail_valid (stream,old,"rename mailbox",LONGT);
  529.   if ((*old != '{') && mail_valid (NIL,new,NIL,LONGT)) {
  530.     sprintf (tmp,"Can't rename to mailbox %s: mailbox already exists",new);
  531.     mm_log (tmp,ERROR);
  532.     return NIL;
  533.   }
  534.   return factory ? (*factory->rename) (stream,old,new) : NIL;
  535. }
  536.  
  537. /* Mail open
  538.  * Accepts: candidate stream for recycling
  539.  *        mailbox name
  540.  *        open options
  541.  * Returns: stream to use on success, NIL on failure
  542.  */
  543.  
  544. MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
  545. {
  546.   DRIVER *factory = mail_valid (NIL,name,(!(stream && stream->silent)) ?
  547.                 "open mailbox" : NIL,(long) NIL);
  548.   if (factory) {        /* must have a factory */
  549.     if (!stream) {        /* instantiate stream if none to recycle */
  550.       if (options & OP_PROTOTYPE) return (*factory->open) (NIL);
  551.       stream = (MAILSTREAM *) fs_get (sizeof (MAILSTREAM));
  552.                 /* initialize stream */
  553.       memset ((void *) stream,0,sizeof (MAILSTREAM));
  554.       stream->dtb = factory;    /* set dispatch */
  555.                 /* set mailbox name */
  556.       stream->mailbox = cpystr (name);
  557.                 /* initialize cache */
  558.       (*mailcache) (stream,(long) 0,CH_INIT);
  559.     }
  560.     else {            /* close driver if different from factory */
  561.       if (stream->dtb != factory) {
  562.     if (stream->dtb) (*stream->dtb->close) (stream);
  563.     stream->dtb = factory;    /* establish factory as our driver */
  564.     stream->local = NIL;    /* flush old driver's local data */
  565.     mail_free_cache (stream);
  566.       }
  567.                 /* clean up old mailbox name for recycling */
  568.       if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  569.       stream->mailbox = cpystr (name);
  570.     }
  571.     stream->lock = NIL;        /* initialize lock and options */
  572.     stream->debug = (options & OP_DEBUG) ? T : NIL;
  573.     stream->readonly = (options & OP_READONLY) ? T : NIL;
  574.     stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
  575.     stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
  576.     stream->silent = (options & OP_SILENT) ? T : NIL;
  577.     stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
  578.                 /* have driver open, flush if failed */
  579.     if (!(*factory->open) (stream)) stream = mail_close (stream);
  580.   }
  581.   return stream;        /* return the stream */
  582. }
  583.  
  584. /* Mail close
  585.  * Accepts: mail stream
  586.  * Returns: NIL
  587.  */
  588.  
  589. MAILSTREAM *mail_close (MAILSTREAM *stream)
  590. {
  591.   if (stream) {            /* make sure argument given */
  592.                 /* do the driver's close action */
  593.     if (stream->dtb) (*stream->dtb->close) (stream);
  594.     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  595.     stream->sequence++;        /* invalidate sequence */
  596.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  597.     mail_free_cache (stream);    /* finally free the stream's storage */
  598.     if (!stream->use) fs_give ((void **) &stream);
  599.   }
  600.   return NIL;
  601. }
  602.  
  603. /* Mail make handle
  604.  * Accepts: mail stream
  605.  * Returns: handle
  606.  *
  607.  *  Handles provide a way to have multiple pointers to a stream yet allow the
  608.  * stream's owner to nuke it or recycle it.
  609.  */
  610.  
  611. MAILHANDLE *mail_makehandle (MAILSTREAM *stream)
  612. {
  613.   MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
  614.   handle->stream = stream;    /* copy stream */
  615.                 /* and its sequence */
  616.   handle->sequence = stream->sequence;
  617.   stream->use++;        /* let stream know another handle exists */
  618.   return handle;
  619. }
  620.  
  621.  
  622. /* Mail release handle
  623.  * Accepts: Mail handle
  624.  */
  625.  
  626. void mail_free_handle (MAILHANDLE **handle)
  627. {
  628.   MAILSTREAM *s;
  629.   if (*handle) {        /* only free if exists */
  630.                 /* resign stream, flush unreferenced zombies */
  631.     if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
  632.     fs_give ((void **) handle);    /* now flush the handle */
  633.   }
  634. }
  635.  
  636.  
  637. /* Mail get stream handle
  638.  * Accepts: Mail handle
  639.  * Returns: mail stream or NIL if stream gone
  640.  */
  641.  
  642. MAILSTREAM *mail_stream (MAILHANDLE *handle)
  643. {
  644.   MAILSTREAM *s = handle->stream;
  645.   return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
  646. }
  647.  
  648. /* Mail fetch long cache element
  649.  * Accepts: mail stream
  650.  *        message # to fetch
  651.  * Returns: long cache element of this message
  652.  * Can also be used to create cache elements for new messages.
  653.  */
  654.  
  655. LONGCACHE *mail_lelt (MAILSTREAM *stream,long msgno)
  656. {
  657.   if (stream->scache) fatal ("Short cache in mail_lelt");
  658.                 /* be sure it the cache is large enough */
  659.   (*mailcache) (stream,msgno,CH_SIZE);
  660.   return (LONGCACHE *) (*mailcache) (stream,msgno,CH_MAKELELT);
  661. }
  662.  
  663.  
  664. /* Mail fetch cache element
  665.  * Accepts: mail stream
  666.  *        message # to fetch
  667.  * Returns: cache element of this message
  668.  * Can also be used to create cache elements for new messages.
  669.  */
  670.  
  671. MESSAGECACHE *mail_elt (MAILSTREAM *stream,long msgno)
  672. {
  673.   if (msgno < 1) fatal ("Bad msgno in mail_elt");
  674.                 /* be sure it the cache is large enough */
  675.   (*mailcache) (stream,msgno,CH_SIZE);
  676.   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
  677. }
  678.  
  679. /* Mail fetch fast information
  680.  * Accepts: mail stream
  681.  *        sequence
  682.  *
  683.  * Generally, mail_fetchstructure is preferred
  684.  */
  685.  
  686. void mail_fetchfast (MAILSTREAM *stream,char *sequence)
  687. {
  688.                   /* do the driver's action */
  689.   if (stream->dtb) (*stream->dtb->fetchfast) (stream,sequence);
  690. }
  691.  
  692.  
  693. /* Mail fetch flags
  694.  * Accepts: mail stream
  695.  *        sequence
  696.  */
  697.  
  698. void mail_fetchflags (MAILSTREAM *stream,char *sequence)
  699. {
  700.                   /* do the driver's action */
  701.   if (stream->dtb) (*stream->dtb->fetchflags) (stream,sequence);
  702. }
  703.  
  704.  
  705. /* Mail fetch message structure
  706.  * Accepts: mail stream
  707.  *        message # to fetch
  708.  *        pointer to return body
  709.  * Returns: envelope of this message, body returned in body value
  710.  *
  711.  * Fetches the "fast" information as well
  712.  */
  713.  
  714. ENVELOPE *mail_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  715. {
  716.   if (msgno < 1 || msgno > stream->nmsgs)
  717.     fatal ("Bad msgno in mail_fetchstructure");
  718.                   /* do the driver's action */
  719.   return stream->dtb ? (*stream->dtb->fetchstructure) (stream,msgno,body) :NIL;
  720. }
  721.  
  722. /* Mail fetch message header
  723.  * Accepts: mail stream
  724.  *        message # to fetch
  725.  * Returns: message header in RFC822 format
  726.  */
  727.  
  728. char *mail_fetchheader (MAILSTREAM *stream,long msgno)
  729. {
  730.   if (msgno < 1 || msgno > stream->nmsgs)
  731.     fatal ("Bad msgno in mail_fetchheader");
  732.                   /* do the driver's action */
  733.   return stream->dtb ? (*stream->dtb->fetchheader) (stream,msgno) : "";
  734. }
  735.  
  736.  
  737. /* Mail fetch message text (body only)
  738.  * Accepts: mail stream
  739.  *        message # to fetch
  740.  * Returns: message text in RFC822 format
  741.  */
  742.  
  743. char *mail_fetchtext (MAILSTREAM *stream,long msgno)
  744. {
  745.   if (msgno < 1 || msgno > stream->nmsgs)
  746.     fatal ("Bad msgno in mail_fetchtext");
  747.                   /* do the driver's action */
  748.   return stream->dtb ? (*stream->dtb->fetchtext) (stream,msgno) : "";
  749. }
  750.  
  751.  
  752. /* Mail fetch message body part text
  753.  * Accepts: mail stream
  754.  *        message # to fetch
  755.  *        section specifier (#.#.#...#)
  756.  *        pointer to returned length
  757.  * Returns: pointer to section of message body
  758.  */
  759.  
  760. char *mail_fetchbody (MAILSTREAM *stream,long m,char *sec,unsigned long *len)
  761. {
  762.   if (m < 1 || m > stream->nmsgs) fatal ("Bad msgno in mail_fetchbody");
  763.                   /* do the driver's action */
  764.   return stream->dtb ? (*stream->dtb->fetchbody) (stream,m,sec,len) : "";
  765. }
  766.  
  767. /* Mail fetch From string for menu
  768.  * Accepts: destination string
  769.  *        mail stream
  770.  *        message # to fetch
  771.  *        desired string length
  772.  * Returns: string of requested length
  773.  */
  774.  
  775. void mail_fetchfrom (char *s,MAILSTREAM *stream,long msgno,long length)
  776. {
  777.   char *t;
  778.   char tmp[MAILTMPLEN];
  779.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  780.   memset (s,' ',length);    /* fill it with spaces */
  781.   s[length] = '\0';        /* tie off with null */
  782.                 /* get first from address from envelope */
  783.   if (env && env->from) {    /* if a personal name exists use it */
  784.     if (!(t = env->from->personal))
  785.       sprintf (t = tmp,"%s@%s",env->from->mailbox,env->from->host);
  786.     memcpy (s,t,min (length,(long) strlen (t)));
  787.   }
  788. }
  789.  
  790.  
  791. /* Mail fetch Subject string for menu
  792.  * Accepts: destination string
  793.  *        mail stream
  794.  *        message # to fetch
  795.  *        desired string length
  796.  * Returns: string of no more than requested length
  797.  */
  798.  
  799. void mail_fetchsubject (char *s,MAILSTREAM *stream,long msgno,long length)
  800. {
  801.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  802.   memset (s,'\0',length+1);
  803.                 /* copy subject from envelope */
  804.   if (env && env->subject) strncpy (s,env->subject,length);
  805.   else *s = ' ';        /* if no subject then just a space */
  806. }
  807.  
  808. /* Mail set flag
  809.  * Accepts: mail stream
  810.  *        sequence
  811.  *        flag(s)
  812.  */
  813.  
  814. void mail_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  815. {
  816.                   /* do the driver's action */
  817.   if (stream->dtb) (*stream->dtb->setflag) (stream,sequence,flag);
  818. }
  819.  
  820.  
  821. /* Mail clear flag
  822.  * Accepts: mail stream
  823.  *        sequence
  824.  *        flag(s)
  825.  */
  826.  
  827. void mail_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  828. {
  829.                   /* do the driver's action */
  830.   if (stream->dtb) (*stream->dtb->clearflag) (stream,sequence,flag);
  831. }
  832.  
  833.  
  834. /* Mail search for messages
  835.  * Accepts: mail stream
  836.  *        search criteria
  837.  */
  838.  
  839. void mail_search (MAILSTREAM *stream,char *criteria)
  840. {
  841.   long i = 1;
  842.   while (i <= stream->nmsgs) mail_elt (stream,i++)->searched = NIL;
  843.                   /* do the driver's action */
  844.   if (stream->dtb) (*stream->dtb->search) (stream,criteria);
  845. }
  846.  
  847.  
  848. /* Mail ping mailbox
  849.  * Accepts: mail stream
  850.  * Returns: stream if still open else NIL
  851.  */
  852.  
  853. long mail_ping (MAILSTREAM *stream)
  854. {
  855.                   /* do the driver's action */
  856.   return stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
  857. }
  858.  
  859. /* Mail check mailbox
  860.  * Accepts: mail stream
  861.  */
  862.  
  863. void mail_check (MAILSTREAM *stream)
  864. {
  865.                   /* do the driver's action */
  866.   if (stream->dtb) (*stream->dtb->check) (stream);
  867. }
  868.  
  869.  
  870. /* Mail expunge mailbox
  871.  * Accepts: mail stream
  872.  */
  873.  
  874. void mail_expunge (MAILSTREAM *stream)
  875. {
  876.                   /* do the driver's action */
  877.   if (stream->dtb) (*stream->dtb->expunge) (stream);
  878. }
  879.  
  880. /* Mail copy message(s)
  881.  * Accepts: mail stream
  882.  *        sequence
  883.  *        destination mailbox
  884.  */
  885.  
  886. long mail_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  887. {
  888.                   /* do the driver's action */
  889.   return stream->dtb ? (*stream->dtb->copy) (stream,sequence,mailbox) : NIL;
  890. }
  891.  
  892.  
  893. /* Mail move message(s)
  894.  * Accepts: mail stream
  895.  *        sequence
  896.  *        destination mailbox
  897.  */
  898.  
  899. long mail_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  900. {
  901.                   /* do the driver's action */
  902.   return stream->dtb ? (*stream->dtb->move) (stream,sequence,mailbox) : NIL;
  903. }
  904.  
  905.  
  906. /* Mail append message string
  907.  * Accepts: mail stream
  908.  *        destination mailbox
  909.  *        stringstruct of message to append
  910.  * Returns: T on success, NIL on failure
  911.  */
  912.  
  913. long mail_append (MAILSTREAM *stream,char *mailbox,STRING *message)
  914. {
  915.   DRIVER *factory = mail_valid (stream,mailbox,"append to mailbox",(long) NIL);
  916.                 /* do the driver's action */
  917.   return factory ? (factory->append) (stream,mailbox,message) : NIL;
  918. }
  919.  
  920. /* Mail garbage collect stream
  921.  * Accepts: mail stream
  922.  *        garbage collection flags
  923.  */
  924.  
  925. void mail_gc (MAILSTREAM *stream,long gcflags)
  926. {
  927.   unsigned long i = 1;
  928.   LONGCACHE *lelt;
  929.                   /* do the driver's action first */
  930.   if (stream->dtb) (*stream->dtb->gc) (stream,gcflags);
  931.   if (gcflags & GC_ENV) {    /* garbage collect envelopes? */
  932.                 /* yes, free long cache if in use */
  933.     if (!stream->scache) while (i <= stream->nmsgs)
  934.       if (lelt = (LONGCACHE *) (*mailcache) (stream,i++,CH_LELT)) {
  935.     mail_free_envelope (&lelt->env);
  936.     mail_free_body (&lelt->body);
  937.       }
  938.     stream->msgno = 0;        /* free this cruft too */
  939.     mail_free_envelope (&stream->env);
  940.     mail_free_body (&stream->body);
  941.   }
  942.                 /* free text if any */
  943.   if ((gcflags & GC_TEXTS) && (stream->text)) fs_give ((void **)&stream->text);
  944. }
  945.  
  946. /* Mail output date from elt fields
  947.  * Accepts: character string to write into
  948.  *        elt to get data data from
  949.  * Returns: the character string
  950.  */
  951.  
  952. const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  953.             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  954.  
  955. char *mail_date (char *string,MESSAGECACHE *elt)
  956. {
  957.   const char *s = (elt->month && elt->month < 13) ?
  958.     months[elt->month - 1] : (const char *) "???";
  959.   sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
  960.        elt->day,s,elt->year + BASEYEAR,
  961.        elt->hours,elt->minutes,elt->seconds,
  962.        elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
  963.   return string;
  964. }
  965.  
  966.  
  967. /* Mail output cdate format date from elt fields
  968.  * Accepts: character string to write into
  969.  *        elt to get data data from
  970.  * Returns: the character string
  971.  */
  972.  
  973. const char *cdays[] = {"Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon"};
  974.  
  975. char *mail_cdate (char *string,MESSAGECACHE *elt)
  976. {
  977.   const char *s = (elt->month && elt->month < 13) ?
  978.     months[elt->month - 1] : (const char *) "???";
  979.   int m = elt->month;
  980.   int y = elt->year + BASEYEAR;
  981.   if (elt->month <= 2) {    /* if before March, */
  982.     m = elt->month + 9;        /* January = month 10 of previous year */
  983.     y--;
  984.   }
  985.   else m = elt->month - 3;    /* March is month 0 */
  986.   sprintf (string,"%s %s %2d %02d:%02d:%02d %4d\n",
  987.        cdays[(int)(elt->day+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100)) % 7],s,
  988.        elt->day,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR);
  989.   return string;
  990. }
  991.  
  992. /* Mail parse date into elt fields
  993.  * Accepts: elt to write into
  994.  *        date string to parse
  995.  * Returns: T if parse successful, else NIL 
  996.  * This routine accepts mm/dd/yy format for the date as well
  997.  */
  998.  
  999. long mail_parse_date (MESSAGECACHE *elt,char *s)
  1000. {
  1001.   long d,m,y;
  1002.   int ms;
  1003.   struct tm *t;
  1004.   time_t tn;
  1005.                 /* skip over possible day of week */
  1006.   if (isalpha (*s) && s[3] == ',') s += 5;
  1007.                 /* parse first number (probable month) */
  1008.   if (!(s && (m = strtol ((const char *) s,&s,10)))) return NIL;
  1009.   switch (*s) {            /* different parse based on delimiter */
  1010.   case '/':            /* mm/dd/yy format */
  1011.     if (!((d = strtol ((const char *) ++s,&s,10)) && *s == '/' &&
  1012.       (y = strtol ((const char *) ++s,&s,10)) && *s == '\0')) return NIL;
  1013.     break;
  1014.   case ' ':            /* dd mmm yy format */
  1015.   case '-':            /* dd-mmm-yy format */
  1016.     d = m;            /* so the number we got is a day */
  1017.                 /* make sure string is UC and long enough! */
  1018.     if (strlen (ucase (s)) < 5) return NIL;
  1019.     /* Some compilers don't allow `<<' and/or longs in case statements. */
  1020.                 /* slurp up the month string */
  1021.     ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
  1022.     switch (ms) {        /* determine the month */
  1023.     case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  1024.     case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  1025.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  1026.     case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  1027.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  1028.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  1029.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  1030.     case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  1031.     case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  1032.     case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
  1033.     case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
  1034.     case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
  1035.     default: return NIL;
  1036.     }
  1037.     if (((s[4] == '-') || s[4] == ' ') &&
  1038.     (y = strtol ((const char *) s+5,&s,10)) && (*s == '\0' || *s == ' '))
  1039.       break;            /* parse the year */
  1040.   default: return NIL;        /* unknown date format */
  1041.   }
  1042.                 /* minimal validity check of date */
  1043.   if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL; 
  1044.                 /* Tenex/ARPAnet began in 1969 */
  1045.   if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  1046.                 /* set values in elt */
  1047.   elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
  1048.  
  1049.   if (*s) {            /* time specification present? */
  1050.                 /* parse time */
  1051.     d = strtol ((const char *) s,&s,10);
  1052.     if (*s != ':') return NIL;
  1053.     m = strtol ((const char *) ++s,&s,10);
  1054.     if (*s != ':') return NIL;
  1055.     y = strtol ((const char *) ++s,&s,10);
  1056.                 /* minimal validity check of time */
  1057.     if (d < 0 || d > 23 || m < 0 || m > 59 || y < 0 || y > 59) return NIL; 
  1058.                 /* set values in elt */
  1059.     elt->hours = d; elt->minutes = m; elt->seconds = y;
  1060.     switch (*s) {        /* time zone specifier? */
  1061.     case '-':            /* symbolic time zone */
  1062.       if (!(ms = *++s)) return NIL;
  1063.       if (*++s) {        /* multi-character? */
  1064.     ms -= 'A'; ms *= 1024;    /* yes, make compressed three-byte form */
  1065.     ms += ((*s++ - 'A') * 32);
  1066.     if (*s) ms += *s++ - 'A';
  1067.     if (*s) return NIL;    /* more than three characters */
  1068.       }
  1069.       /* This is not intended to be a comprehensive list of all possible
  1070.        * timezone strings.  Such a list would be impractical.  Rather, this
  1071.        * listing is intended to incorporate all military, north American, and
  1072.        * a few special cases such as Japan and the major European zone names,
  1073.        * such as what might be expected to be found in a Tenex format mailbox
  1074.        * and spewed from an IMAP server.  The trend is to migrate to numeric
  1075.        * timezones which lack the flavor but also the ambiguity of the names.
  1076.        */
  1077.       switch (ms) {        /* determine the timezone */
  1078.                 /* Middle Europe */
  1079.       case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1080.       case 'A': elt->zhours = 1; break;
  1081.                 /* Eastern Europe */
  1082.       case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1083.       case 'B': elt->zhours = 2; break;
  1084.       case 'C': elt->zhours = 3; break;
  1085.       case 'D': elt->zhours = 4; break;
  1086.       case 'E': elt->zhours = 5; break;
  1087.       case 'F': elt->zhours = 6; break;
  1088.       case 'G': elt->zhours = 7; break;
  1089.       case 'H': elt->zhours = 8; break;
  1090.                 /* Japan */
  1091.       case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1092.       case 'I': elt->zhours = 9; break;
  1093.       case 'K': elt->zhours = 10; break;
  1094.       case 'L': elt->zhours = 11; break;
  1095.       case 'M': elt->zhours = 12; break;
  1096.  
  1097.       case 'N': elt->zoccident = 1; elt->zhours = 1; break;
  1098.       case 'O': elt->zoccident = 1; elt->zhours = 2; break;
  1099.       case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1100.       case 'P': elt->zoccident = 1; elt->zhours = 3; break;
  1101.                 /* Atlantic */
  1102.       case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1103.       case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1104.       case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
  1105.                 /* Eastern */
  1106.       case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1107.       case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1108.       case 'R': elt->zoccident = 1; elt->zhours = 5; break;
  1109.                 /* Central */
  1110.       case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1111.       case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1112.       case 'S': elt->zoccident = 1; elt->zhours = 6; break;
  1113.                 /* Mountain */
  1114.       case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1115.       case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1116.       case 'T': elt->zoccident = 1; elt->zhours = 7; break;
  1117.                 /* Pacific */
  1118.       case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1119.       case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1120.       case 'U': elt->zoccident = 1; elt->zhours = 8; break;
  1121.                 /* Yukon */
  1122.       case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1123.       case (('H'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1124.       case 'V': elt->zoccident = 1; elt->zhours = 9; break;
  1125.                 /* Hawaii */
  1126.       case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1127.       case (('B'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1128.       case 'W': elt->zoccident = 1; elt->zhours = 10; break;
  1129.                 /* Bering */
  1130.       case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1131.       case 'X': elt->zoccident = 1; elt->zhours = 11; break;
  1132.       case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
  1133.                 /* Universal */
  1134.       case (('U'-'A')*1024)+(('T'-'A')*32):
  1135.       case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
  1136.       case 'Z': elt->zhours = 0; break;
  1137.  
  1138.       case (('L'-'A')*1024) + (('C'-'A')*32)+'L'-'A':
  1139.     /* I knew I was going to regret that idiotic `LCL' stuff someday!! */
  1140.     tn = time (0);        /* time now... */
  1141.     t = localtime (&tn);    /* get local minutes since midnight */
  1142.     m = t->tm_hour * 60 + t->tm_min;
  1143.     t = gmtime (&tn);    /* minus UTC minutes since midnight */
  1144.                 /* add a day if too far past */
  1145.     if ((m -= t->tm_hour * 60 + t->tm_min) < -720) m += 1440;
  1146.     if (m > 720) m -= 1440;    /* subtract a day if too far ahead */
  1147.     if (m < 0) {        /* occidental? */
  1148.       m = abs (m);        /* yup, make positive number */
  1149.       elt->zoccident = 1;    /* and note west of UTC */
  1150.     }
  1151.     elt->zhours = m / 60;    /* now break into hours and minutes */
  1152.     elt->zminutes = m % 60;
  1153.     break;
  1154.       default:            /* unknown time zone name */
  1155.     return NIL;
  1156.       }
  1157.       elt->zminutes = 0;    /* never a fractional hour */
  1158.       break;
  1159.     case '\0':            /* no time zone */
  1160.       break;
  1161.     case ' ':            /* numeric time zone */
  1162.                 /* test for sign character */
  1163.       if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
  1164.       if (!(isdigit (*s) && isdigit (s[1]) && isdigit (s[2]) && isdigit (s[3]))
  1165.        || s[4]) return NIL;    /* proper timezone */
  1166.       elt->zhours = (*s - '0') * 10 + (s[1] - '0');
  1167.       elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
  1168.       break;
  1169.     default:
  1170.       return NIL;
  1171.     }
  1172.   }
  1173.   return T;
  1174. }
  1175.  
  1176. /* Mail messages have been searched out
  1177.  * Accepts: mail stream
  1178.  *        message number
  1179.  *
  1180.  * Calls external "mm_searched" function to notify main program
  1181.  */
  1182.  
  1183. void mail_searched (MAILSTREAM *stream,long msgno)
  1184. {
  1185.                 /* mark as searched */
  1186.   mail_elt (stream,msgno)->searched = T;
  1187.   mm_searched (stream,msgno);    /* notify main program */
  1188. }
  1189.  
  1190.  
  1191. /* Mail n messages exist
  1192.  * Accepts: mail stream
  1193.  *        number of messages
  1194.  *
  1195.  * Calls external "mm_exists" function that notifies main program prior
  1196.  * to updating the stream
  1197.  */
  1198.  
  1199. void mail_exists (MAILSTREAM *stream,long nmsgs)
  1200. {
  1201.                 /* make sure cache is large enough */
  1202.   (*mailcache) (stream,nmsgs,CH_SIZE);
  1203.                 /* notify main program of change */
  1204.   if (!stream->silent) mm_exists (stream,nmsgs);
  1205.   stream->nmsgs = nmsgs;    /* update stream status */
  1206. }
  1207.  
  1208. /* Mail n messages are recent
  1209.  * Accepts: mail stream
  1210.  *        number of recent messages
  1211.  */
  1212.  
  1213. void mail_recent (MAILSTREAM *stream,long recent)
  1214. {
  1215.   stream->recent = recent;    /* update stream status */
  1216. }
  1217.  
  1218.  
  1219. /* Mail message n is expunged
  1220.  * Accepts: mail stream
  1221.  *        message #
  1222.  *
  1223.  * Calls external "mm_expunged" function that notifies main program prior
  1224.  * to updating the stream
  1225.  */
  1226.  
  1227. void mail_expunged (MAILSTREAM *stream,long msgno)
  1228. {
  1229.   long i = msgno - 1;
  1230.   MESSAGECACHE *elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
  1231.   if (elt) {            /* if an element is there */
  1232.     elt->msgno = 0;        /* invalidate its message number and free */
  1233.     (*mailcache) (stream,msgno,CH_FREE);
  1234.   }
  1235.                 /* expunge the slot */
  1236.   (*mailcache) (stream,msgno,CH_EXPUNGE);
  1237.   --stream->nmsgs;        /* update stream status */
  1238.                 /* notify main program of change */
  1239.   if (!stream->silent) mm_expunged (stream,msgno);
  1240. }
  1241.  
  1242. /* mail stream status routines */
  1243.  
  1244.  
  1245. /* Mail lock stream
  1246.  * Accepts: mail stream
  1247.  */
  1248.  
  1249. void mail_lock (MAILSTREAM *stream)
  1250. {
  1251.   if (stream->lock) fatal ("Lock when already locked");
  1252.   else stream->lock = T;    /* lock stream */
  1253. }
  1254.  
  1255.  
  1256. /* Mail unlock stream
  1257.  * Accepts: mail stream
  1258.  */
  1259.  
  1260. void mail_unlock (MAILSTREAM *stream)
  1261. {
  1262.   if (!stream->lock) fatal ("Unlock when not locked");
  1263.   else stream->lock = NIL;    /* unlock stream */
  1264. }
  1265.  
  1266.  
  1267. /* Mail turn on debugging telemetry
  1268.  * Accepts: mail stream
  1269.  */
  1270.  
  1271. void mail_debug (MAILSTREAM *stream)
  1272. {
  1273.   stream->debug = T;        /* turn on debugging telemetry */
  1274. }
  1275.  
  1276.  
  1277. /* Mail turn off debugging telemetry
  1278.  * Accepts: mail stream
  1279.  */
  1280.  
  1281. void mail_nodebug (MAILSTREAM *stream)
  1282. {
  1283.   stream->debug = NIL;        /* turn off debugging telemetry */
  1284. }
  1285.  
  1286. /* Mail parse sequence
  1287.  * Accepts: mail stream
  1288.  *        sequence to parse
  1289.  * Returns: T if parse successful, else NIL
  1290.  */
  1291.  
  1292. long mail_sequence (MAILSTREAM *stream,char *sequence)
  1293. {
  1294.   long i,j,x;
  1295.   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
  1296.   while (*sequence) {        /* while there is something to parse */
  1297.                 /* parse and validate message number */
  1298.     if (((i = (int) strtol ((const char *) sequence,&sequence,10)) < 1) ||
  1299.     (i > stream->nmsgs)) {
  1300.       mm_log ("Sequence invalid",ERROR);
  1301.       return NIL;
  1302.     }
  1303.     switch (*sequence) {    /* see what the delimiter is */
  1304.     case ':':            /* sequence range */
  1305.                 /* parse end of range */
  1306.       if (((j = (int) strtol ((const char *) ++sequence,&sequence,10)) < 1) ||
  1307.       (j > stream->nmsgs) || (*sequence && *sequence++ != ',')) {
  1308.     mm_log ("Sequence range invalid",ERROR);
  1309.     return NIL;
  1310.       }
  1311.       if (i > j) {        /* swap the range if backwards */
  1312.     x = i; i = j; j = x;
  1313.       }
  1314.                 /* mark each item in the sequence */
  1315.       while (i <= j) mail_elt (stream,j--)->sequence = T;
  1316.       break;
  1317.     case ',':            /* single message */
  1318.       ++sequence;        /* skip the delimiter, fall into end case */
  1319.     case '\0':            /* end of sequence, mark this message */
  1320.       mail_elt (stream,i)->sequence = T;
  1321.       break;
  1322.     default:            /* anything else is a syntax error! */
  1323.       mm_log ("Syntax error in sequence",ERROR);
  1324.       return NIL;
  1325.     }
  1326.   }
  1327.   return T;            /* successfully parsed sequence */
  1328. }
  1329.  
  1330. /* Mail data structure instantiation routines */
  1331.  
  1332.  
  1333. /* Mail instantiate envelope
  1334.  * Returns: new envelope
  1335.  */
  1336.  
  1337. ENVELOPE *mail_newenvelope ()
  1338. {
  1339.   ENVELOPE *env = (ENVELOPE *) fs_get (sizeof (ENVELOPE));
  1340.   env->remail = NIL;        /* initialize all fields */
  1341.   env->return_path = NIL;
  1342.   env->date = NIL;
  1343.   env->subject = NIL;
  1344.   env->from = env->sender = env->reply_to = env->to = env->cc = env->bcc = NIL;
  1345.   env->in_reply_to = env->message_id = env->newsgroups = NIL;
  1346.   return env;
  1347. }
  1348.  
  1349.  
  1350. /* Mail instantiate address
  1351.  * Returns: new address
  1352.  */
  1353.  
  1354. ADDRESS *mail_newaddr ()
  1355. {
  1356.   ADDRESS *adr = (ADDRESS *) fs_get (sizeof (ADDRESS));
  1357.                 /* initialize all fields */
  1358.   adr->personal = adr->adl = adr->mailbox = adr->host = adr->error = NIL;
  1359.   adr->next = NIL;
  1360.   return adr;
  1361. }
  1362.  
  1363.  
  1364. /* Mail instantiate body
  1365.  * Returns: new body
  1366.  */
  1367.  
  1368. BODY *mail_newbody ()
  1369. {
  1370.   return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
  1371. }
  1372.  
  1373. /* Mail initialize body
  1374.  * Accepts: body
  1375.  * Returns: body
  1376.  */
  1377.  
  1378. BODY *mail_initbody (BODY *body)
  1379. {
  1380.   body->type = TYPETEXT;    /* content type */
  1381.   body->encoding = ENC7BIT;    /* content encoding */
  1382.   body->subtype = body->id = body->description = NIL;
  1383.   body->parameter = NIL;
  1384.   body->contents.text = NIL;    /* no contents yet */
  1385.   body->contents.binary = NIL;
  1386.   body->contents.part = NIL;
  1387.   body->contents.msg.env = NIL;
  1388.   body->contents.msg.body = NIL;
  1389.   body->contents.msg.text = NIL;
  1390.   body->size.lines = body->size.bytes = body->size.ibytes = 0;
  1391.   return body;
  1392. }
  1393.  
  1394.  
  1395. /* Mail instantiate body parameter
  1396.  * Returns: new body part
  1397.  */
  1398.  
  1399. PARAMETER *mail_newbody_parameter ()
  1400. {
  1401.   PARAMETER *parameter = (PARAMETER *) fs_get (sizeof (PARAMETER));
  1402.   parameter->attribute = parameter->value = NIL;
  1403.   parameter->next = NIL;    /* no next yet */
  1404.   return parameter;
  1405. }
  1406.  
  1407.  
  1408. /* Mail instantiate body part
  1409.  * Returns: new body part
  1410.  */
  1411.  
  1412. PART *mail_newbody_part ()
  1413. {
  1414.   PART *part = (PART *) fs_get (sizeof (PART));
  1415.   mail_initbody (&part->body);    /* initialize the body */
  1416.   part->offset = 0;        /* no offset yet */
  1417.   part->next = NIL;        /* no next yet */
  1418.   return part;
  1419. }
  1420.  
  1421. /* Mail garbage collection routines */
  1422.  
  1423.  
  1424. /* Mail garbage collect body
  1425.  * Accepts: pointer to body pointer
  1426.  */
  1427.  
  1428. void mail_free_body (BODY **body)
  1429. {
  1430.   if (*body) {            /* only free if exists */
  1431.     mail_free_body_data (*body);/* free its data */
  1432.     fs_give ((void **) body);    /* return body to free storage */
  1433.   }
  1434. }
  1435.  
  1436.  
  1437. /* Mail garbage collect body data
  1438.  * Accepts: body pointer
  1439.  */
  1440.  
  1441. void mail_free_body_data (BODY *body)
  1442. {
  1443.   if (body->subtype) fs_give ((void **) &body->subtype);
  1444.   mail_free_body_parameter (&body->parameter);
  1445.   if (body->id) fs_give ((void **) &body->id);
  1446.   if (body->description) fs_give ((void **) &body->description);
  1447.   switch (body->type) {        /* free contents */
  1448.   case TYPETEXT:        /* unformatted text */
  1449.     if (body->contents.text) fs_give ((void **) &body->contents.text);
  1450.     break;
  1451.   case TYPEMULTIPART:        /* multiple part */
  1452.     mail_free_body_part (&body->contents.part);
  1453.     break;
  1454.   case TYPEMESSAGE:        /* encapsulated message */
  1455.     mail_free_envelope (&body->contents.msg.env);
  1456.     mail_free_body (&body->contents.msg.body);
  1457.     if (body->contents.msg.text)
  1458.       fs_give ((void **) &body->contents.msg.text);
  1459.     break;
  1460.   case TYPEAPPLICATION:        /* application data */
  1461.   case TYPEAUDIO:        /* audio */
  1462.   case TYPEIMAGE:        /* static image */
  1463.   case TYPEVIDEO:        /* video */
  1464.     if (body->contents.binary) fs_give (&body->contents.binary);
  1465.     break;
  1466.   default:
  1467.     break;
  1468.   }
  1469. }
  1470.  
  1471. /* Mail garbage collect body parameter
  1472.  * Accepts: pointer to body parameter pointer
  1473.  */
  1474.  
  1475. void mail_free_body_parameter (PARAMETER **parameter)
  1476. {
  1477.   if (*parameter) {        /* only free if exists */
  1478.     if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
  1479.     if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
  1480.                 /* run down the list as necessary */
  1481.     mail_free_body_parameter (&(*parameter)->next);
  1482.                 /* return body part to free storage */
  1483.     fs_give ((void **) parameter);
  1484.   }
  1485. }
  1486.  
  1487.  
  1488. /* Mail garbage collect body part
  1489.  * Accepts: pointer to body part pointer
  1490.  */
  1491.  
  1492. void mail_free_body_part (PART **part)
  1493. {
  1494.   if (*part) {            /* only free if exists */
  1495.     mail_free_body_data (&(*part)->body);
  1496.                 /* run down the list as necessary */
  1497.     mail_free_body_part (&(*part)->next);
  1498.     fs_give ((void **) part);    /* return body part to free storage */
  1499.   }
  1500. }
  1501.  
  1502. /* Mail garbage collect message cache
  1503.  * Accepts: mail stream
  1504.  *
  1505.  * The message cache is set to NIL when this function finishes.
  1506.  */
  1507.  
  1508. void mail_free_cache (MAILSTREAM *stream)
  1509. {
  1510.                 /* flush the cache */
  1511.   (*mailcache) (stream,(long) 0,CH_INIT);
  1512.   stream->msgno = 0;        /* free this cruft too */
  1513.   mail_free_envelope (&stream->env);
  1514.   mail_free_body (&stream->body);
  1515.   if (stream->text) fs_give ((void **) &stream->text);
  1516. }
  1517.  
  1518.  
  1519. /* Mail garbage collect cache element
  1520.  * Accepts: pointer to cache element pointer
  1521.  */
  1522.  
  1523. void mail_free_elt (MESSAGECACHE **elt)
  1524. {
  1525.                 /* only free if exists and no sharers */
  1526.   if (*elt && !--(*elt)->lockcount) fs_give ((void **) elt);
  1527.   else *elt = NIL;        /* else simply drop pointer */
  1528. }
  1529.  
  1530.  
  1531. /* Mail garbage collect long cache element
  1532.  * Accepts: pointer to long cache element pointer
  1533.  */
  1534.  
  1535. void mail_free_lelt (LONGCACHE **lelt)
  1536. {
  1537.                 /* only free if exists and no sharers */
  1538.   if (*lelt && !--(*lelt)->elt.lockcount) {
  1539.     mail_free_envelope (&(*lelt)->env);
  1540.     mail_free_body (&(*lelt)->body);
  1541.     fs_give ((void **) lelt);    /* return cache element to free storage */
  1542.   }
  1543.   else *lelt = NIL;        /* else simply drop pointer */
  1544. }
  1545.  
  1546. /* Mail garbage collect envelope
  1547.  * Accepts: pointer to envelope pointer
  1548.  */
  1549.  
  1550. void mail_free_envelope (ENVELOPE **env)
  1551. {
  1552.   if (*env) {            /* only free if exists */
  1553.     if ((*env)->remail) fs_give ((void **) &(*env)->remail);
  1554.     mail_free_address (&(*env)->return_path);
  1555.     if ((*env)->date) fs_give ((void **) &(*env)->date);
  1556.     mail_free_address (&(*env)->from);
  1557.     mail_free_address (&(*env)->sender);
  1558.     mail_free_address (&(*env)->reply_to);
  1559.     if ((*env)->subject) fs_give ((void **) &(*env)->subject);
  1560.     mail_free_address (&(*env)->to);
  1561.     mail_free_address (&(*env)->cc);
  1562.     mail_free_address (&(*env)->bcc);
  1563.     if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
  1564.     if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
  1565.     if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
  1566.     fs_give ((void **) env);    /* return envelope to free storage */
  1567.   }
  1568. }
  1569.  
  1570.  
  1571. /* Mail garbage collect address
  1572.  * Accepts: pointer to address pointer
  1573.  */
  1574.  
  1575. void mail_free_address (ADDRESS **address)
  1576. {
  1577.   if (*address) {        /* only free if exists */
  1578.     if ((*address)->personal) fs_give ((void **) &(*address)->personal);
  1579.     if ((*address)->adl) fs_give ((void **) &(*address)->adl);
  1580.     if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
  1581.     if ((*address)->host) fs_give ((void **) &(*address)->host);
  1582.     if ((*address)->error) fs_give ((void **) &(*address)->error);
  1583.     mail_free_address (&(*address)->next);
  1584.     fs_give ((void **) address);/* return address to free storage */
  1585.   }
  1586. }
  1587.